/**
 * 作用：webpack打包生成的runtime文件太小了，额外发送请求性能不好，所以需要将其内联到js中，从而减少请求。
 * 开发思路：
 * 1、需要借助html-webpack-plugin来实现
 *    在html-webpack-plugin输出index.html前将内联runtime注入
 *    删除多余的runtime文件
 * 2、如何操作html-webpack-plugin?
 *    官方文档 https://github.com/jantimon/html-webpack-plugin
 */
const HtmlWebpackPlugin = require('safe-require')('html-webpack-plugin')

class InlineChunkWebpackPlugin {
  constructor(options = {regExps: []}) {
    // regExps 中包括要匹配的文件名正则
    this.$options = options
  }

  apply(compiler) {
    compiler.hooks.compilation.tap('InlineChunkWebpackPlugin', (compilation) => {
      // 获取html-webpack-plugin的hooks
      const hooks = HtmlWebpackPlugin.getHooks(compilation)
      // 注册html-webpack-plugin的hook -> alterAssetTagGroups
      hooks.alterAssetTagGroups.tap('InlineChunkWebpackPlugin', (assets) => {
        // 将script的runtime文件变成inline script
        assets.headTags = this.getInlineChunk(assets.headTags, compilation.assets)
        assets.bodyTags = this.getInlineChunk(assets.bodyTags, compilation.assets)
      })
      // 删除runtime文件
      hooks.afterEmit.tap('InlineChunkWebpackPlugin', () => {
        Object.keys(compilation.assets).forEach(filepath => {
          // 是否满足文件名要求
          if (this.options.regExps.some(reg => reg.test(filepath))) {
            delete compilation.assert[filepath]
          }
        })
      })
    })
  }

  /**
   * 获取行内块
   * @param tags
   * @param assets
   * @returns {*}
   */
  getInlineChunk(tags, assets) {
    return tags.map(tag => {
      // 是否是script标签
      if (tag.tagName !== 'script') return tag
      // 是否是外联标签
      const filepath = tag.attributes.src
      if (!filepath) return tag
      // 是否满足文件名要求
      if (this.options.regExps.every(reg => reg.test(filepath))) return tag
      // 返回更改后的行内标签设置
      return {
        tagName: 'script',
        innerHtml: assets[filepath].source(),
        closeTag: true
      }
    })
  }
}

module.exports = InlineChunkWebpackPlugin
